package testrank;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import testrank.affinity.AffinityCalculator;
import testrank.affinity.AffinityPreprocessor;
import testrank.predictors.AveragePredictor;

/**
 * <pre>
 * cd TestRanking
 * //java testrank.TestRank pre project/test [-file filename]
 * java -cp bin;../lib/aspectjrt.jar testrank.TestRank core
 * java testrank.TestRank query source-file[:line] [-file filename]
 * e.g.
 * java -cp . testrank.TestRank query LogicalOperator.java:67
 * java -cp bin;../lib/aspectjrt.jar testrank.TestRank query LogicalOperator.java:67
 * </pre>
 * 
 * @author Hagai Cibulski
 * 
 */
public class TestRank {

	private static final boolean VERBOSE_SCORE_BY_RANK = false;
	
	public static void main2(String[] args) {
		//main1("affinity", "LoggerRepository org.apache.log4j.LogManager.getLoggerRepository()", "void org.apache.log4j.net.TelnetAppenderTest.testIt()");
	}
	/**
	 * @param args
	 */
	public static void main(String... args) {
		if (args.length > 1 && args[0].equals("query")) {
			String[] location = args[1].split(":");
			if (location.length == 2) {
				String fileName = location[0];
				String lineString = location[1];
				int line = Integer.parseInt(lineString);
				
				TestManager.load();
				doQuery(fileName, line);
			}
		}
		else
		if (args.length > 0 && args[0].equals("core")) {
			TestManager.load();
			reportCoreMethods();
		}
		else
		if (args.length > 0 && args[0].equals("tests")) {
			String q = (args.length > 1) ? args[1] : null;
			TestManager.load();
			reportAllTests(q == null, q);
		}
		else
		if (args.length > 0 && args[0].equals("pre")) {
			TestManager.load();
			preprocessAffinities();
		}
		else
		if (args.length > 2 && args[0].equals("affinity")) {
			String methodSignature = args[1];
			String testSignature = args[2];
			Conflict conflict = new Conflict(methodSignature, testSignature);
			
			TestManager.load();
			AffinityPreprocessor processor = new AffinityPreprocessor();
			try {
				processor.process();
			}
			catch (IOException e) {
				e.printStackTrace();// TODO
			}
			TestManager.setProcessor(processor);
			AffinityCalculator calc = new AffinityCalculator(); 
			double affinity = 
				TestManager.affinityPredictor().getConflictAffinity(calc, conflict, true);
			System.out.println("affinity="+affinity);
		}
	}


	private static void doQuery(String fileName, int line) {
		String methodSignature = TestManager.locator().locateMethod(fileName, line);
		if (methodSignature == null) {
			System.out.println("No tests affected.");
			return;
		}
		System.out.println("methodSignature=" + methodSignature);
		
		System.out.println("ExecutionCountPredictor (99%):");
		doQuery(TestManager.predictor(), methodSignature);
		doQuery(AveragePredictor.create("EXCNT99").put("EXCNT", 0.99).put("AFFIN", 0.01), methodSignature);
		

		System.out.println("AffinityPredictor:");
		doQuery(TestManager.affinityPredictor(), methodSignature);

		System.out.println("AveragePredictor:");
		doQuery(new AveragePredictor(), methodSignature);
	}
	
	private static void doQuery(Predictor predictor, String methodSignature) {
		SortedMap<Double, List<String>> testByRank = rankTests(predictor,
				methodSignature);
		
		printResults(testByRank);
		
//		System.out.println(testByRank.toString().replaceAll("],", "],\n"));
	}

	public static SortedMap<Double, List<String>> rankTests(Predictor predictor,
			String methodSignature) {
	
		List<String> allTests = TestManager.predictor().getAllTests();
		SortedMap<Double, List<String>> testByRank = new TreeMap<Double, List<String>>();
		int count = 0;
		for (String testSignature : allTests) {
			double rank = predictor.rankFailure(methodSignature, testSignature);

			if (TestManager.predictor().isCovering(methodSignature, testSignature)) {//rank > 0.0) { // rank of zero is assumed to be a non covering test
				
			count++;
			//	rank += (Math.abs(testSignature.hashCode()) % 77777) / 100000000.0;
			//	rank += (Math.abs(testSignature.hashCode()) % 7777) / 1000000000.0;
				
				List<String> testList = testByRank.get(rank);
				if (testList == null) {
					testList = new ArrayList<String>();
					testByRank.put(rank, testList);
				}
				testList.add(testSignature);
			}
		}
		
		if (VERBOSE_SCORE_BY_RANK) {
			System.out.println(predictor.getName());
			System.out.println(testByRank.toString().replace("],", "],\n"));
		}
		
		return testByRank;
	}

	public static void printResults(SortedMap<Double, List<String>> testByRank) {
		String result = testByRank.toString();
		String[] splitList = result.split("],");
		int position = 1;
		for (int i = splitList.length - 1; i >= 0; i--) {
			System.out.println(position++ + ") " + splitList[i]);
		}
	}
	

	
	/**
	 * reports methods under test ordered by test coverage 
	 */
	private static void reportCoreMethods() {
		Set<Conflict> conflicts = TestManager.predictor().getAllConflicts();
		Map<String, Integer> counts = new HashMap<String, Integer>();
		for (Conflict conflict : conflicts) {
			String method = conflict.getMethodSignature();
			Integer count = counts.get(method);
			if (count == null) {
				count = 0;
			}
			count++;
			counts.put(method, count);
		}
		
		class MethodCount implements Comparable<MethodCount> {
			int count;
			String method;
			@Override
			public int compareTo(MethodCount o) {
				return o.count - count; // reverse order
			}
			@Override
			public String toString() {
				return "MethodCount [count=" + this.count + ", method="
						+ this.method + "]";
			}
		}
		
		List<MethodCount> list = new ArrayList<MethodCount>();
		for (String m : counts.keySet()) {
			MethodCount mc = new MethodCount();
			mc.method = m;
			mc.count = counts.get(m);
			list.add(mc);
		}
		Collections.sort(list);
		for (MethodCount mc : list) {
			System.out.println(mc);
		}	
	}

	/**
	 * reports all tests (for Jumble input) 
	 */
	private static void reportAllTests(boolean onlyClass, String q) {
		SortedSet<String> allTests = new TreeSet<String>();  
		Set<Conflict> conflicts = TestManager.predictor().getAllConflicts();
		for (Conflict conflict : conflicts) {
			String test = conflict.getTestSignature();
			if (q != null) {
				String methodSignature = conflict.getMethodSignature();
				if ( ! methodSignature.contains(q)) {
					continue;
				}
			}
			if (onlyClass) {
				int testMethodPos = test.lastIndexOf('.');
				String testClass = test.substring(5, testMethodPos);
				if ( ! testClass.endsWith("AbstractTest")) {
					allTests.add(testClass);
				}
			}
			else {
				allTests.add(test);
			}
		}
		if (onlyClass)
			System.out.println(allTests.toString().replaceAll("(),", ""));
		else
			System.out.println(allTests.toString().replaceAll("(),", "(),\n"));
		System.out.println("size="+allTests.size());
	}

	
	
	/**
	 * entry point for pre-processing affinities, after dynamic pre-processing is done 
	 */
	public static void preprocessAffinities() {
		System.out.println(TestManager.predictor().toString());
		System.out.println(TestManager.locator().toString());
		
		AffinityPreprocessor processor = new AffinityPreprocessor();
		try {
			processor.process();
		}
		catch (IOException e) {
			e.printStackTrace();// TODO
		}
		TestManager.setProcessor(processor);
		
		long startProcess = System.currentTimeMillis();
		TestManager.affinityPredictor().process();
		long processTime = System.currentTimeMillis() - startProcess;
		System.out.println("AffinityPredictor.process() took " + processTime/1000 + " seconds");
		TestManager.affinityPropagationPredictor().process();
		
//		TestManager.predictor().processScores();
		
		TestManager.save();
	}
}
